/* Copyright (C) 2021 Rain */

/* This file is part of XNIX. */

/* 
  XNIX is free software: you can redistribute it and/or modify 
  it under the terms of the GNU General Public License as published by 
  the Free Software Foundation, either version 3 of the License, or 
  (at your option) and later version. 
*/

/*
  XNIX is distributed in the hope that it will be useful, 
  but WITHOUT ANY WARRANTY; without even the implied warranty of 
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
  GNU General Public License for more details. 
*/

/*
  You should have received a copy of the GNU General Public License 
   along with XNIX. If not, see <https://www.gnu.org/licenses/>.  
*/




#include <kernel/init.h>
#include <kernel/errno.h>
#include <kernel/types.h>

#include <kernel/console.h>
#include <kernel/int.h>
#include <kernel/panic.h>

#include <arch/x86/int.h>
#include <arch/io.h>
#include <arch/sys.h>


build_irq(0x20);
build_irq(0x21);
build_irq(0x22);
build_irq(0x23);
build_irq(0x24);
build_irq(0x25);
build_irq(0x26);
build_irq(0x27);
build_irq(0x28);
build_irq(0x29);
build_irq(0x2a);
build_irq(0x2b);
build_irq(0x2c);
build_irq(0x2d);
build_irq(0x2e);
build_irq(0x2f);


struct irq_entry irq_table[MAX_IRQ_NR];

void set_irq(int nr, const void *handler) {
	if (nr > 16) 
		panic("set_irq: bad nr. ");

	irq_table[nr].handler = handler;

	return;
}


void register_irq(int nr, void *entry, const void *handler) {
	if (nr > 16) 
		panic("register_irq: bad nr. ");

	if (irq_table[nr].handler) 
		return;

	__uint8_t mask;
	
	if (nr < 8) {
		mask = inb(0x21) & ~(1 << nr);
		outb(mask, 0x21);
	}

	else {
		/* enable slave-irq */
		mask = inb(0x21) & ~(1 << 2);
		outb(mask, 0x21);

		mask = inb(0xa1) & ~(1 << (nr - 8));
		outb(mask, 0xa1);
	}


	irq_table[nr].handler = handler;

	/* IRQ nr starts at 0x20 */
	set_int_gate(idt_table + 0x20 + nr, 0, entry);

	return;
}


void unregister_irq(int nr) {
	__uint8_t mask;

	irq_table[nr].handler = NULL;

	if (nr < 8) {
		mask = inb(0x21) | 1 << nr;
		outb(mask, 0x21);
	}

	else {
		mask = inb(0xa1) | 1 << (nr - 8);
		outb(mask, 0xa1);
	}

	return;
}


void do_irq(const __uint64_t *rsp, __uint64_t nr) {
	void (*fn) (void) = irq_table[nr - 0x20].handler;

	if (fn) 
		fn();

	else 
		printk(red, "unknow irq %x\n", nr - 0x20);

	if (nr > 0x27) {
		/* slave-irq ok */
		outb(0x62, PIC0_OCW2);
		/* this.irqnr ok */
		outb(0x60 - 0x28 + nr, PIC1_OCW2);
	}

	else 
		outb(0x60 - 0x20 + nr, PIC0_OCW2);

	return;
}

void trace_irq(void) {
	printk(yellow, "tracing irq: \n");

	__uint8_t slave = 0, master = 0;

	int i;

	master = inb(0x21);
	slave = inb(0xa1);

	printk(yellow, "\tmaster: %b  slave: %b\n");

	for (i = 0; i < 8; i++) 
		if (!(master & (1 << i))) 
			printk(yellow, "\tirq %b (master) enabled. \n", i);

	for (i = 0; i < 8; i++) 
		if (!(slave & (1 << i))) 
			printk(yellow, "\tirq %b (slave) enabled. \n", i + 8);

	return;
}

